package com.neusoft.util;

import java.io.UnsupportedEncodingException;

/**
 * 使用SHA-256加密HTTP摘要信息工具类
 * 
 * @author  郑兴龙 zheng-xl@neusoft.com
 */
public final class SHA256DigestUtil
{
    /**
     * 请求方式
     */
    private static final String METHOD = "POST";
    
    /**
     * <默认构造函数>
     */
    private SHA256DigestUtil()
    {
    }
    
    /**
     * 加密生成鉴权中的response值
     * 
     * <br>A1 - userName:realm:password
     * <br>A2 - nonce:nc:cnonce:qop
     * <br>A3 - METHOD:url or METHOD:url:SHA256Encrypt.encrypt(new String(body)) or ""
     * 
     * <br>SHA256Encrypt.encrypt(SHA256Encrypt.encrypt(A1):A2:SHA256Encrypt.encrypt(A3))
     * 
     * @param digest HTTP摘要认证鉴权参数实体类
     * @param body HTTP请求的消息体
     *
     * @return 加密后的值 - SHA256Encrypt.encrypt(SHA256Encrypt.encrypt(A1):A2:SHA256Encrypt.encrypt(A3))
     */
    public static String generateResponse(AuthenticateDigest digest, byte[] body)
    {
        /*
         * @param userName 约定的用户名
         * @param realm 约定的域
         * @param nonce 服务器用来鉴定客户端的摘要质询参数
         * @param url  访问路径
         * @param qop auth：鉴权，不对消息体做完整性验证。auth-int：鉴权并需要对消息体做摘要，保证消息完整性。注册过程使用auth
         * @param nc 客户端请求的十六进制计数，以00000001开始，每次请求加1，目的是防止重放攻击
         * @param cnonce 客户端用来鉴定服务器的摘要质询参数
         * @param password 约定的密码
         */
        String userName = digest.getUserName();
        String realm = digest.getRealm();
        String nonce = digest.getNonce();
        String url = digest.getUri();
        String qop = digest.getQop();
        String nc = digest.getNc();
        String cnonce = digest.getCnonce();
        String password = digest.getPassword();
        
        return SHA256Encrypt.encrypt(generateSHA256(userName, realm, password) + ':'
            + generateA2(nonce, nc, cnonce, qop) + ':' + SHA256Encrypt.encrypt(generateA3(qop, url, body)));
    }
    
    /**
     * 构造userName,realm,password
     * <br>SHA256Encrypt.encrypt(A1)
     * @param userName 约定的用户名
     * @param realm 约定的域
     * @param password 约定的密码
     * 
     * @return 返回 SHA256Encrypt.encrypt(A1) 值
     */
    private static String generateSHA256(String userName, String realm, String password)
    {
        // 对A1用SHA-256加密
        return SHA256Encrypt.encrypt(generateA1(userName, realm, password));
    }
    
    /**
     * 返回A1的value
     * @param userName 约定的用户名
     * @param realm 约定的域
     * @param password 约定的密码
     * @return userName:realm:password
     * @see [类、类#方法、类#成员]
     */
    private static String generateA1(String userName, String realm, String password)
    {
        return unq(userName) + ':' + unq(realm) + ':' + unq(password);
    }
    
    /**
     * 构造nonce,nc,cnonce,qop字符串
     * <br>A2 - nonce:nc:cnonce:qop
     * @param nonce 服务器用来鉴定客户端的摘要质询参数
     * @param nc 客户端请求的十六进制计数
     * @param cnonce 客户端用来鉴定服务器的摘要质询参数
     * @param qop 鉴权(auth or auth-int)
     * @return 返回 A2值
     */
    private static String generateA2(String nonce, String nc, String cnonce, String qop)
    {
        return unq(nonce) + ':' + nc + ':' + unq(cnonce) + ':' + unq(qop);
    }
    
    /**
     * 根据qop权值组合字符串
     * <br>A3 - METHOD:url or METHOD:url:SHA256Encrypt.encrypt(new String(body)) or ""
     * @param qop 鉴权(auth or auth-int)
     * @param url 访问路径
     * @param body http请求体
     * @return 返回A3值
     * <br>qop is auth     ---> A3 is METHOD:url
     * <br>qop is auth-int ---> A3 is METHOD:url:SHA256Encrypt.encrypt(new String(body))
     * <br>qop is other    ---> A3 is ""
     */
    private static String generateA3(String qop, String url, byte[] body)
    {
        if ("auth".equals(qop))
        {
            return METHOD + ':' + url;
        }
        else if ("auth-int".equals(qop))
        {
            String entityBody = "";
            
            try
            {
                entityBody = new String(body, "utf-8");
            }
            catch (UnsupportedEncodingException e)
            {
                entityBody = new String(body);
            }
            
            return METHOD + ':' + url + ':' + SHA256Encrypt.encrypt(entityBody);
        }
        
        return "";
    }
    
    /**
     * 去掉引号(有几去掉,没有就不去掉)
     * @param value 值
     * @return 去掉引号的值
     * @see [类、类#方法、类#成员]
     */
    private static String unq(String value)
    {
        return (value + "").replace("\"", "");
    }
}
